<template>
    <div id="containerId"></div>
</template>

<script setup>
import { onMounted } from "vue";
// 原理：基于 canvas 标签绘制像素点（位置，颜色等）在平面上计算出 3D 效果
// 下载并引入three库
import * as THREE from "three";
// 引入轨道控制器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 引入性能监视器
import Stats from "three/examples/jsm/libs/stats.module.js";

// 全局变量
let scene;  // 存储场景
let camera; // 存储摄像机
let renderer;   // 存储渲染器
let controls;   // 存储轨道控制器
let cubeArr=[]; // 存储多个立方体
let statsArr=[]; // 存储性能监视器 

onMounted(() => {
    // 1.场景：放置物体的容器（世界）
    scene = new THREE.Scene();
    // 2.摄像机：类似于人眼，决定看到多大的空间范围
    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    // 3.渲染器：接收`场景`和`摄像机`，计算在浏览器上渲染的最终`2D画面`。
    renderer = new THREE.WebGLRenderer({antialias: true});
    // 3.1 将输出canvas的大小调整为(width, height)
    renderer.setSize( window.innerWidth, window.innerHeight );
    // 添加到页面中
    document.getElementById("containerId").appendChild( renderer.domElement );

    // 创建坐标轴，并添加场景世界中
    scene.add(createAxes(10))

    // 生成多个立方体的数据
    let cubeDataList = new Array(5).fill(1).map(item => {
        let positionMax = 5, positionMin = -5, sizeMax = 3, sizeMin = 1
        return {
            width:  Math.floor(Math.random() * (sizeMax - sizeMin + 1) + sizeMin),
            height: Math.floor(Math.random() * (sizeMax - sizeMin + 1) + sizeMin),
            deep:   Math.floor(Math.random() * (sizeMax - sizeMin + 1) + sizeMin),
            x: Math.floor(Math.random() * (positionMax - positionMin + 1) + positionMin),
            y: Math.floor(Math.random() * (positionMax - positionMin + 1) + positionMin),
            z: Math.floor(Math.random() * (positionMax - positionMin + 1) + positionMin),
            color: `rgb(${Math.floor(Math.random() * 256)},${Math.floor(Math.random() * 256)},${Math.floor(Math.random() * 256)})`
        }
    })
    // 生成多个立方体并添加到场景中
    cubeDataList.forEach(item => {
        let cube = createCube(item)
        scene.add(cube);
        cubeArr.push(cube);
    })

    // 创建性能监视器
    for(let i=0; i<3; i++) {
        let sta = createStats(i, `${90*2 - 90*i}px`, 0);
        document.getElementById("containerId").appendChild(sta.domElement)
        statsArr.push(sta)
    }


    // 创建轨道控制器
    controls = createOrbit(camera, renderer.domElement)
    // 调用动画
    animate();

    // 移动摄像机
    camera.position.set( 2, 2, 15 )
    // 把 场景+摄像机 传递给渲染器进行渲染
    renderer.render(scene, camera)

    resize();
})

/**
 * 页面变化后重新绘制
*/
function resize() {
    window.addEventListener("resize", () => {
        // 设置画布（世界黑色背景）的宽高
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 调整摄像机宽高比（防止被压缩）
        camera.aspect = window.innerWidth / window.innerHeight
        // 更新摄像机矩阵空间（让摄像机重新计算每个像素点位置和大小）
        camera.updateProjectionMatrix();
    })
}


/**
 * 添加动画效果
*/
function animate() {
    // 根据浏览器刷新帧率（默认1s 60次）不断递归调用此渲染函数，为了让render不断渲染最新的画面
    requestAnimationFrame( animate );
    // 轨道控制器属性改变，需要更新轨道控制器才有效果
    controls.update();
    // 给每一个立方体添加按照自身Y轴旋转的功能
    cubeArr.forEach(itemCube => {
        itemCube.rotation.y += 0.01
    })
    // 不断更新当下最新 FPF 等状态数值
    statsArr.forEach(item => {
        item.update();
    })
    renderer.render( scene, camera );
}

/**
 * 创建立方体
*/
function createCube({width=1, height=1, deep=1, x=0, y=0, z=0, color= 0x00ff00}={}) {
    // 1.几何图形 BoxGeometry 立方体（宽、高、深）1单位大小（three.js独有单位）
    const geometry = new THREE.BoxGeometry(width, height, deep);
    // 2.材质（皮肤）：决定我要一个什么样子的立方体（颜色）
    // MeshBasicMaterial 网格（面）基础（纯颜色）材质（0x 16进制颜色）
    const material = new THREE.MeshBasicMaterial({ color: color });
    // 3.创建物体（Mesh）网格材质（面）
    const cube = new THREE.Mesh(geometry, material);
    cube.position.x = x;
    cube.position.y = y;
    cube.position.z = z;
    return cube;
}


/**
 * 创建五颜六色立方体
*/
function createCubeColorFul({width=1, height=1, deep=1,x=0, y=0, z=0,colorArr= ["red", "green", "blue", "pink", "orange", "white"]}={}) {
    // 1.几何图形 BoxGeometry 立方体（宽、高、深）1单位大小（three.js独有单位）
    const geometry = new THREE.BoxGeometry(width, height, deep);
    // 2.材质（皮肤）：决定我要一个什么样子的立方体（颜色）
    let materialArr = colorArr.map(item => {
        // MeshBasicMaterial 网格（面）基础（纯颜色）材质（0x 16进制颜色）
        return new THREE.MeshBasicMaterial({ color: item });
    })
    // 3.创建物体（Mesh）网格材质（面）
    const cube = new THREE.Mesh(geometry, materialArr);
    cube.position.x = x;
    cube.position.y = y;
    cube.position.z = z;
    return cube;
}


/**
 * 创建球体
*/
function createSphere() {
    const geometry = new THREE.SphereGeometry( 2, 32, 16 );
    const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
    const sphere = new THREE.Mesh( geometry, material );
    return sphere
}


/**
 * 创建坐标轴
*/
function createAxes(size=1) {
    const axesHelper = new THREE.AxesHelper( size );
    return axesHelper
}


/**
 * 创建轨道控制器
*/
function createOrbit (camera, domElement) {
    const controls = new OrbitControls( camera, domElement )
    controls.update();
    return controls
}

/**
 * 创建性能监视器
*/
function createStats(type, right, top) {
    let stats = new Stats();
    stats.setMode(type); // 0:每秒刷新帧数，1：每帧用时，2：内存占用
    stats.domElement.style.position = "fixed";
    stats.domElement.style.width = "fit-content";
    // 取消掉原先的left样式，否则后面设置的right样式不生效
    stats.domElement.style.left = "";
    stats.domElement.style.right = right;
    stats.domElement.style.top = top;
    return stats
}
</script>